package service

import (
	"context"
	"fmt"
	"gitee.com/cristiane/micro-mall-order/model/mysql"
	"gitee.com/cristiane/micro-mall-order/pkg/code"
	"gitee.com/cristiane/micro-mall-order/repository"
	"gitee.com/kelvins-io/kelvins"
	"github.com/seata/seata-go/pkg/rm/tcc"
	"github.com/seata/seata-go/pkg/tm"
	"strconv"
	"time"
)

type TradeOrderTcc struct {
	proxy  *tcc.TCCServiceProxy
	params interface{}
}

func NewTradeOrderTcc(params interface{}) (*TradeOrderTcc, error) {
	b := &TradeOrderTcc{
		params: params,
	}
	proxy, err := tcc.NewTCCServiceProxy(b)
	b.proxy = proxy
	return b, err
}

func (b *TradeOrderTcc) Prepare(ctx context.Context, params interface{}) (ok bool, err error) {
	// try幂等检查
	xid := tm.GetXID(ctx)
	bid := strconv.FormatInt(tm.GetBusinessActionContext(ctx).BranchId, 10)
	{
		tryIdempotentSql := "id"
		tryIdempotentWhere := map[string]interface{}{
			"xid":      xid,
			"bid":      bid,
			"business": "TradeOrderTcc",
		}
		tryIdempotentRecord, err := repository.GetTccBranchTxRecord(tryIdempotentSql, tryIdempotentWhere)
		if err != nil {
			kelvins.ErrLogger.Errorf(ctx, "TradeOrderTcc Prepare GetTccBranchTxRecord err: %v, params:xid %v bid:%v", err, xid, bid)
			return false, err
		}
		// 已经有执行记录了
		if len(tryIdempotentRecord) == 0 {
			var tcRecord *mysql.TccBranchTransactionRecord
			tcRecord = &mysql.TccBranchTransactionRecord{
				Business:   "TradeOrderTcc",
				XID:        xid,
				BID:        bid,
				UpdateTime: time.Now(),
				CreateTime: time.Now(),
			}
			err = repository.CreateTccBranchTxRecord(tcRecord)
			if err != nil {
				kelvins.ErrLogger.Errorf(ctx, "TradeOrderTcc Prepare CreateTccBranchTxRecord err: %v, params:xid %v bid:%v", err, xid, bid)
				return false, err
			}
		}
	}

	// try 幂等检查
	tryIdempotentSql := "id"
	tryIdempotentWhere := map[string]interface{}{
		"business":   "TradeOrderTcc",
		"xid":        xid,
		"bid":        bid,
		"try_status": 2, // try 已经执行成功
	}
	tryIdempotentRecord, err := repository.GetTccBranchTxRecord(tryIdempotentSql, tryIdempotentWhere)
	if err != nil {
		kelvins.ErrLogger.Errorf(ctx, "TradeOrderTcc Prepare GetTccBranchTxRecord err: %v, params:xid %v bid:%v", err, xid, bid)
		return false, err
	}
	// 已经有执行记录了
	if len(tryIdempotentRecord) != 0 {
		return true, nil
	}
	// try执行结果
	defer func() {
		maps := map[string]interface{}{
			"try_status":  1,
			"update_time": time.Now(),
		}
		if !ok || err != nil {
			maps["try_status"] = 1
		} else {
			maps["try_status"] = 2
		}
		where := map[string]interface{}{
			"xid":      xid,
			"bid":      bid,
			"business": "TradeOrderTcc",
		}
		_, err = repository.UpdateTccBranchTxRecord(where, maps)
		if err != nil {
			kelvins.ErrLogger.Errorf(ctx, "TradeOrderTcc Prepare UpdateTccBranchTxRecord err: %v, params:xid %v bid:%v", err, xid, bid)
			return
		}
	}()

	// try 悬挂处理
	trySuspendSql := "id"
	trySuspendWhere := map[string]interface{}{
		"xid":           xid,
		"bid":           bid,
		"cancel_status": 2, // cancel 已经执行成功
	}
	trySuspendRecord, err := repository.GetTccBranchTxRecord(trySuspendSql, trySuspendWhere)
	if err != nil {
		kelvins.ErrLogger.Errorf(ctx, "TradeOrderTcc Prepare GetTccBranchTxRecord err: %v, params:xid %v bid:%v", err, xid, bid)
		return false, err
	}
	// 已经有执行记录了
	if len(trySuspendRecord) != 0 {
		return true, nil
	}

	// 本地事务
	req := params.(*CreateOrderTccRequest)
	result, retCode := createOrder(ctx, req.originalReq)
	req.result = result
	req.retCode = retCode
	if retCode == code.Success {
		ok = true
		err = nil
		return
	}
	return false, fmt.Errorf(code.ErrMap[retCode])
}

func (b *TradeOrderTcc) Commit(ctx context.Context, businessActionContext *tm.BusinessActionContext) (bool, error) {
	return true, nil
}

func (b *TradeOrderTcc) Rollback(ctx context.Context, businessActionContext *tm.BusinessActionContext) (ok bool, err error) {
	// 插入一条记录
	xid := businessActionContext.Xid
	bid := strconv.FormatInt(businessActionContext.BranchId, 10)
	{
		tryIdempotentSql := "id"
		tryIdempotentWhere := map[string]interface{}{
			"xid":      xid,
			"bid":      bid,
			"business": "TradeOrderTcc",
		}
		tryIdempotentRecord, err := repository.GetTccBranchTxRecord(tryIdempotentSql, tryIdempotentWhere)
		if err != nil {
			kelvins.ErrLogger.Errorf(ctx, "TradeOrderTcc Rollback GetTccBranchTxRecord err: %v, params:xid %v bid:%v", err, xid, bid)
			return false, err
		}
		// 已经有执行记录了
		if len(tryIdempotentRecord) == 0 {
			var tcRecord *mysql.TccBranchTransactionRecord
			tcRecord = &mysql.TccBranchTransactionRecord{
				Business:   "TradeOrderTcc",
				XID:        xid,
				BID:        bid,
				UpdateTime: time.Now(),
				CreateTime: time.Now(),
			}
			err = repository.CreateTccBranchTxRecord(tcRecord)
			if err != nil {
				kelvins.ErrLogger.Errorf(ctx, "TradeOrderTcc Rollback CreateTccBranchTxRecord err: %v, params:xid %v bid:%v", err, xid, bid)
				return false, err
			}
		}
	}

	// cancel 幂等检查
	cancelIdempotentSql := "id"
	cancelIdempotentWhere := map[string]interface{}{
		"xid":           xid,
		"bid":           bid,
		"business":      "TradeOrderTcc",
		"cancel_status": 2, // try 已经执行成功
	}
	tryIdempotentRecord, err := repository.GetTccBranchTxRecord(cancelIdempotentSql, cancelIdempotentWhere)
	if err != nil {
		kelvins.ErrLogger.Errorf(ctx, "TradeOrderTcc Rollback GetTccBranchTxRecord err: %v, params:xid %v bid:%v", err, xid, bid)
		return false, err
	}
	// 已经有执行记录了
	if len(tryIdempotentRecord) != 0 {
		return true, nil
	}

	// cancel 空回滚处理
	cancelRbSql := "id"
	cancelRbWhere := map[string]interface{}{
		"xid":        xid,
		"bid":        bid,
		"business":   "TradeOrderTcc",
		"try_status": "1",
	}
	cancelRbRecord, err := repository.GetTccBranchTxRecord(cancelRbSql, cancelRbWhere)
	if err != nil {
		kelvins.ErrLogger.Errorf(ctx, "TradeOrderTcc Rollback GetTccBranchTxRecord err: %v, params:xid %v bid:%v", err, xid, bid)
		return false, err
	}
	// 该事务记录不存在 说明 try还没有执行 或者没有执行失败
	if len(cancelRbRecord) == 0 {
		return true, nil
	}

	// 执行完毕 确定结果
	defer func() {
		maps := map[string]interface{}{
			"cancel_status": 1,
			"update_time":   time.Now(),
		}
		if !ok || err != nil {
			maps["cancel_status"] = 1
		} else {
			maps["cancel_status"] = 2
		}
		where := map[string]interface{}{
			"xid":      xid,
			"bid":      bid,
			"business": "TradeOrderTcc",
		}
		_, err = repository.UpdateTccBranchTxRecord(where, maps)
		if err != nil {
			kelvins.ErrLogger.Errorf(ctx, "TradeOrderTcc Rollback UpdateTccBranchTxRecord err: %v, params:xid %v bid:%v", err, xid, bid)
			return
		}
	}()

	// 取消订单
	deductReq := b.params.(*CreateOrderTccRequest)
	retCode := tradeOrderCancel(ctx, deductReq.originalReq.OrderTxCode)
	if retCode == code.Success {
		ok = true
		err = nil
		return
	}

	return false, err
}
